package com.mvc.framework.dao;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Date;

import javax.persistence.Entity;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.event.DeleteEvent;
import org.hibernate.event.def.DefaultDeleteEventListener;
import org.hibernate.metadata.ClassMetadata;

import com.mvc.framework.config.ProjectConfig;
import com.mvc.framework.model.Auditable;
import com.mvc.framework.model.CommonAuditableEntity;
import com.mvc.framework.model.CompositeKeyAuditableEntity;
import com.mvc.framework.util.FrameworkContextUtils;
import com.mvc.framework.util.HibernateUtils;

public class AuditListenerForDelete extends DefaultDeleteEventListener {
	private static final Logger LOGGER = Logger.getLogger(AuditListenerForDelete.class);

	public void onDelete(DeleteEvent event) throws HibernateException {
		Object object = event.getObject();
		Integer userId = FrameworkContextUtils.getCurrentUserId();
		if (object instanceof CommonAuditableEntity) {
			CommonAuditableEntity entity = (CommonAuditableEntity) object;
			Session session = event.getSession();
			entity.setUpdatedOn(new Date());
			entity.setUpdatedBy(userId);
			entity.setRecStatus(CommonAuditableEntity.REC_STATUS_INVALID);
			StringBuilder hql = new StringBuilder();
			Entity entityClass = object.getClass().getAnnotation(Entity.class);
			hql.append("UPDATE ").append(entityClass.name()).append(" SET REC_STATUS = ").append(
					CommonAuditableEntity.REC_STATUS_INVALID).append(",UPDATED_ON = ?");
			if (null != userId) {
				hql.append(",UPDATED_BY = ?");
			}
			hql.append(" WHERE ID = ").append(entity.getId());
			SQLQuery query = session.createSQLQuery(hql.toString());
			query.setParameter(0, entity.getUpdatedOn());
			if (null != userId) {
				query.setParameter(1, entity.getUpdatedBy());
			}
			try {
				query.executeUpdate();
			} catch (HibernateException e) {
				LOGGER.error("onDelete",e);
			}
		}else if(object instanceof CompositeKeyAuditableEntity){
			Session session = event.getSession();
			SessionFactory sessionFactory = session.getSessionFactory();
			ClassMetadata classMetadata = sessionFactory.getClassMetadata(object.getClass());
			Object idClass = classMetadata.getIdentifier(object, (SessionImplementor) session);
			Field[] fields = idClass.getClass().getDeclaredFields();
			Entity entityClass = object.getClass().getAnnotation(Entity.class);
			//设置更新人和更新时间
			StringBuilder hql = new StringBuilder();
			hql.append("UPDATE ").append(entityClass.name()).append(" set recStatus = ").append(
					Auditable.REC_STATUS_INVALID).append(",updatedOn = ?");
			if (null != userId) {
				hql.append(",updatedBy = ?");
			}
			hql.append(" WHERE ");
			StringBuilder where = new StringBuilder();
			for(Field field : fields){
				String primaryKey = field.getName();
				try {
					Method method = object.getClass().getDeclaredMethod("get" + Character.toUpperCase(primaryKey.charAt(0)) + primaryKey.substring(1));
					if (!Modifier.isPublic(method.getModifiers())) {
						method.setAccessible(true);
					}
					Object value = method.invoke(object);
					where.append(primaryKey).append("=").append(value).append(" and ");
                } catch (Exception e) {
                	LOGGER.error("onDelete",e);
                }
			}
			hql.append(where.substring(0,where.length()-5));
			Query query = session.createQuery( hql.toString() );
			query.setParameter(0, new Date());
			if (null != userId) {
				query.setParameter(1, userId);
			}
			try {
				query.executeUpdate();
			} catch (HibernateException e) {
				LOGGER.error("onDelete",e);
			}
			//save history
			String queryHql = " SELECT A from " + entityClass.name() + " A where " + where.substring(0,where.length()-5);
	    	String querySql = HibernateUtils.hqlToSql(sessionFactory, queryHql);
	    	StringBuilder sql = new StringBuilder(40);
	    	sql.append("INSERT INTO ").append(entityClass.name()).append(ProjectConfig.HISTORY_TABLE_SUFFIX).append(" ").append(querySql);
			query = session.createSQLQuery(sql.toString());
			query.executeUpdate();
			//delete entity
			super.onDelete(event);
		} else {
			super.onDelete(event);
		}
	}
}
